﻿using ParboiledSharp.parboiled.rules;
using ParboiledSharp.lang.roboline.utils;

namespace ParboiledSharp.lang.roboline.grammar
{
    partial class RoboLineGrammar : BaseParser
    {
        private const char S_QUOTESYMBOL = StringUtils.QUOTESYMBOL;

        #region TestRules
        public Rule TestExpression()
        {
            return SEQUENCE(
                        Expression(),
                        EOI()
                   );
        }

        public Rule TestAssignemnt()
        {
            return SEQUENCE(
                        Assignment(),
                        EOI()
                   );
        }

        public Rule TestBlock()
        {
            return SEQUENCE(
                        Block(),
                        EOI()
                   );
        }
        #endregion

        public Rule Expression()
        {
            return Rule(delegate()
            {
                return FIRST(
                    DynamicObjectCreation(),
                    SEQUENCE(Lambda(), OPTIONAL(ArgumentList())),
                    SEQUENCE(ComparsionLevel(),
                                ZEROORMORE(
                                        FIRST( 
                                            SEQUENCE(
                                                OR(),
                                                ComparsionLevel(),
                                                ___pushOr()
                                            ),
                                            SEQUENCE(
                                                AND(),
                                                ComparsionLevel(),
                                                ___pushAnd()
                                            )
                                        )
                                )
                       ));
            });
        }

        public Rule Return()
        {
            return Rule(delegate()
            {
                return SEQUENCE(RETURN_TERMINAL(), Expression());
            });
        }

        public Rule Assignment()
        {
            return Rule(delegate()
            {
                return FIRST(
                        SEQUENCE(VarScope(), Variable(), ASSIGNMENTSYMBOL(), Expression()),
                        SEQUENCE(Increment()),
                        SEQUENCE(Decrement())
                        );
            });
        }

        public Rule VarDeclaration()
        {
            return Rule(delegate()
            {
                return SEQUENCE(VarScope(), ID());
            });
        }

        public Rule If()
        {
            return Rule(delegate()
            {
                return SEQUENCE(IF_TERMINAL(), LEFTBRACKET(), Expression(), RIGHTBRACKET(), BlockWithWings(),
                                                                            ZEROORMORE(SEQUENCE(ELSE_TERMINAL(), IF_TERMINAL(), LEFTBRACKET(), Expression(), RIGHTBRACKET(), BlockWithWings())),
                                                                            OPTIONAL(SEQUENCE(ELSE_TERMINAL(), BlockWithWings())));
            });
        }

        public Rule ForArguments()
        {
            return Rule(delegate()
            {
                return SEQUENCE(OPTIONAL(SEQUENCE(Assignment(), ZEROORMORE(SEQUENCE(COMMA(), Assignment())))),
                                SEMICOLON(),
                                OPTIONAL(Expression()),
                                SEMICOLON(),
                                OPTIONAL(FIRST(Assignment(), Expression())));
            });
        }

        public Rule For()
        {
            return Rule(delegate()
            {
                return SEQUENCE(FOR_TERMINAL(), LEFTBRACKET(), ForArguments(), RIGHTBRACKET(), BlockWithWings());
            });
        }

        public Rule BlockWithWings()
        {
            return Rule(delegate()
            {
                return SEQUENCE(LEFTWING(), Block(), RIGHTWING());
            });
        }

        public Rule Block()
        {
            return Rule(delegate()
            {
                return ZEROORMORE(
                    FIRST(
                         SEQUENCE(VarDeclaration(), SEMICOLON()),
                         SEQUENCE(Return(), SEMICOLON()),
                         If(),
                         For(),
                         SEMICOLON(),
                         SEQUENCE(Expression(), SEMICOLON()),///TODO:CLEAR STACK AFTER THIS RULE
                         SEQUENCE(Assignment(), SEMICOLON())
                         )
                    );
            });
        }

        public Rule ComparsionLevel()
        {
            return Rule(delegate()
            {
                return SEQUENCE(PLUS_MINUS_LEVEL(),
                                ZEROORMORE(
                                        SEQUENCE(
                                            SEQUENCE( FIRST(LESSOREQUAL(), GREATEROREQUAL(), LESS(), GREATER(), EQUAL(), NOTEQUAL()), PUSH("EQUALITY_OPERATOR")),
                                            PLUS_MINUS_LEVEL()
                                        , ___pushCompare())
                                )
                       );
            });
        }

        public Rule PLUS_MINUS_LEVEL()
        {
            return Rule(delegate()
            {
                return SEQUENCE(MUL_DIV_LEVEL(),
                                ZEROORMORE(
                                        SEQUENCE(
                                            SEQUENCE(FIRST(PLUS(), MINUS()), PUSH("PLUS_MINUS_OPERATOR")),
                                            MUL_DIV_LEVEL(), ___pushAddSub())
                                )
                                );
            });
        }

        public Rule MUL_DIV_LEVEL()
        {
            return Rule(delegate()
            {
                return SEQUENCE(SEQUENCE(Factor(),
                                ZEROORMORE(
                                        SEQUENCE(
                                            SEQUENCE(FIRST(MULTIPLY(), DIVIDE()), PUSH("MUL_DIV_OPERATOR")),
                                            Factor(),___pushMulDiv())
                                    )
                               ), OPTIONAL(ArgumentList()));//This OPTIONAL is made for function execution
            });
        }

        public Rule Factor()
        {
            return Rule(delegate()
            {
                return FIRST(
                        TRUE_TERMINAL(),
                        FALSE_TERMINAL(),
                        Variable(),
                        SEQUENCE(MINUS(), Expression(), ___pushUnaryMinus()),
                        STRINGTERMINAL(),
                        Number(),
                        Parens());
            });
        }

        public Rule Increment()
        {
            return Rule(delegate()
            {
                return SEQUENCE(Variable(), PLUSPLUS(), ___pushIncrement());
            });
        }

        public Rule Decrement()
        {
            return Rule(delegate()
            {
                return SEQUENCE(Variable(), MINUSMINUS(), ___pushDecrement());
            });
        }

        public Rule Parens()
        {
            return Rule(delegate()
            {
                return SEQUENCE(
                    LEFTBRACKET(),
                    Expression(),
                    RIGHTBRACKET());
            });
        }

        public Rule DynamicProperty()
        {
            return Rule(delegate()
                {
                    return SEQUENCE(ID(),
                                    COLON(),
                                    Expression());
                });
        }
        public Rule DynamicObjectCreation()
        {
            return Rule(delegate()
            {
                return SEQUENCE(
                    LEFTWING(),
                    OPTIONAL(SEQUENCE(DynamicProperty(),
                                    ZEROORMORE(SEQUENCE(COMMA(), DynamicProperty())))),
                    RIGHTWING());
            });
        }   

        public Rule LambdaClosureVariables()
        {
            return Rule(delegate()
            {
                return SEQUENCE(
                                LEFTSQUAREBRACKET(),
                                OPTIONAL(FormalParamsList()),
                                RIGHTSQUAREBRACKET());
            });
        }

        public Rule LambdaFormalParams()
        {
            return Rule(delegate()
            {
                return SEQUENCE(
                                LEFTBRACKET(),
                                OPTIONAL(FormalParamsList()),
                                RIGHTBRACKET());
            });
        }

        public Rule FormalParamsList()
        {
            return Rule(delegate()
            {
                return SEQUENCE(Variable(),
                                   ZEROORMORE(
                                         SEQUENCE(COMMA(), Variable())));
            });
        }

        public Rule Lambda()
        {
            return Rule(delegate()
            {
                return SEQUENCE(APOS(), OPTIONAL(LambdaFormalParams()), OPTIONAL(LambdaClosureVariables()), BlockWithWings());
            });
        }

        public Rule ArgumentList()
        {
            return Rule(delegate()
            {
                return SEQUENCE(
                    LEFTBRACKET(),
                    OPTIONAL(
                            SEQUENCE(
                                Expression(),
                                ZEROORMORE(SEQUENCE(COMMA(), Expression()))
                                     )),
                    RIGHTBRACKET());
            });
        }

        public Rule STRINGTERMINAL()
        {
            return SEQUENCE(
                        WhiteSpace(),
                        S_QUOTESYMBOL,
                        ZEROORMORE(SEQUENCE(TESTNOT(FIRST(S_QUOTESYMBOL, EOI())), ANY())),
                                        ___pushStringRule(),
                        S_QUOTESYMBOL,
                        WhiteSpace());
        }

        public Rule Number()
        {
            return Rule(delegate()
            {
                return SEQUENCE(WhiteSpace(),
                                FIRST(
                                    DigitsDouble(),
                                    DigitsInteger()
                                    ),
                                WhiteSpace());
            });
        }

        public Rule DigitsInteger()
        {
            return Rule(delegate()
            {
                return SEQUENCE(ONEORMORE(Digit()), ___pushIntegerRule());
            });
        }

        public Rule DigitsDouble()
        {
            return Rule(delegate()
            {
                return SEQUENCE(SEQUENCE(ONEORMORE(Digit()), DOT(), ONEORMORE(Digit())), ___pushDoubleRule());
            });
        }

        public Rule Variable()
        {
            return SEQUENCE(ID(), OPTIONAL(
                        ZEROORMORE(FIRST(
                                    SEQUENCE(DOT(), ID()),
                                    SEQUENCE(LEFTSQUAREBRACKET(), Expression(), RIGHTSQUAREBRACKET())
                            )))
                        );
        }

        public Rule VarScope()
        {
            return Rule(delegate()
            {
                return FIRST(
                    NONE(LOCALVAR(), GLOBALVAR()),
                    LOCALVAR(), 
                    GLOBALVAR()
                    );
            });
        }

        public Rule ID()
        {
            return SEQUENCE(WhiteSpace(),
                        ALLOWEDCHARS(),
                        ZEROORMORE(FIRST(
                                        Digit(),
                                        ALLOWEDCHARS()
                                        )
                                   ), WhiteSpace()
                           );
        }

        public Rule TERMINAL(string term)
        {
            return SEQUENCE(
                        WhiteSpace(),
                        STRING_IGNORE_CASE(term),
                        WhiteSpace());
        }

        public Rule TERMINAL(string term1, string term2)
        {
            return SEQUENCE(
                        WhiteSpace(),
                        FIRST(STRING_IGNORE_CASE(term1), STRING_IGNORE_CASE(term2)),
                        WhiteSpace());
        }
        ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //////////////////////////////////////////////////TERMINALS////////////////////////////////////////////////////
        ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
        #region Terminals
        public Rule WhiteSpace()
        {
            return ZEROORMORE(FIRST(' ', '\t', '\n', '\r'));
        }

        public Rule Digit()
        {
            return CHARRANGE('0', '9');
        }

        public Rule ALLOWEDCHARS()
        {
            return FIRST(CHARRANGE('a', 'z'), CHARRANGE('A', 'Z'), CHARRANGE('а', 'я'), CHARRANGE('А', 'Я'), "_", "$", "@", "&", "#", "№", "|");
        }

        public Rule LEFTBRACKET()
        {
            return TERMINAL("(");
        }

        public Rule RIGHTBRACKET()
        {
            return TERMINAL(")");
        }

        public Rule LEFTWING()
        {
            return TERMINAL("{");
        }

        public Rule RIGHTWING()
        {
            return TERMINAL("}");
        }

        public Rule LEFTSQUAREBRACKET()
        {
            return TERMINAL("[");
        }

        public Rule RIGHTSQUAREBRACKET()
        {
            return TERMINAL("]");
        }

        public Rule PLUS()
        {
            return TERMINAL("+");
        }

        public Rule PLUSPLUS()
        {
            return TERMINAL("++");
        }

        public Rule MINUS()
        {
            return TERMINAL("-");
        }

        public Rule MINUSMINUS()
        {
            return TERMINAL("--");
        }

        public Rule DIVIDE()
        {
            return TERMINAL("/");
        }

        public Rule MULTIPLY()
        {
            return TERMINAL("*");
        }

        public Rule LESS()
        {
            return TERMINAL("<");
        }

        public Rule GREATER()
        {
            return TERMINAL(">");
        }

        public Rule EQUAL()
        {
            return TERMINAL("==");
        }

        public Rule LESSOREQUAL()
        {
            return TERMINAL("<=");
        }

        public Rule GREATEROREQUAL()
        {
            return TERMINAL(">=");
        }

        public Rule NOTEQUAL()
        {
            return TERMINAL("!=");
        }

        public Rule SEMICOLON()
        {
            return TERMINAL(";");
        }

        public Rule COLON()
        {
            return TERMINAL(":");
        }

        public Rule DOT()
        {
            return TERMINAL(".");
        }

        public Rule COMMA()
        {
            return TERMINAL(",");
        }

        public Rule APOS()
        {
            return TERMINAL("'");
        }

        public Rule ASSIGNMENTSYMBOL()
        {
            return TERMINAL("=");
        }

        public Rule QUOTE()
        {
            return STRING(S_QUOTESYMBOL);
        }

        public Rule IF_TERMINAL()
        {
            return TERMINAL("if", "если");
        }

        public Rule ELSE_TERMINAL()
        {
            return TERMINAL("else", "иначе");
        }

        public Rule FOR_TERMINAL()
        {
            return TERMINAL("for", "цикл");
        }

        public Rule AND()
        {
            return TERMINAL("and", "и");
        }

        public Rule OR()
        {
            return TERMINAL("or", "или");
        }

        public Rule NOT()
        {
            return TERMINAL("not", "нет");
        }

        public Rule BREAK()
        {
            return TERMINAL("break", "прекратить");
        }

        public Rule CONTINUE()
        {
            return TERMINAL("continue", "продолжить");
        }

        public Rule RETURN_TERMINAL()
        {
            return TERMINAL("return", "вернуть");
        }

        public Rule LOCALVAR()
        {
            return FIRST(
                        TERMINAL("%"),
                        TERMINAL("local", "локальная"));
        }

        public Rule GLOBALVAR()
        {
            return TERMINAL("global", "глобальная");
        }

        public Rule TRUE_TERMINAL()
        {
            return SEQUENCE(TERMINAL("true", "да"),___pushTrueFalseRule(true));
        }

        public Rule FALSE_TERMINAL()
        {
            return SEQUENCE(TERMINAL("false", "нет"),___pushTrueFalseRule(false));
        }
        #endregion
    }
}
